package com.uuah.server.lifecycle;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.set.ListOrderedSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.AbstractUuahException;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.lifecycle.ILifecycleManager;
import com.uuah.server.api.lifecycle.ILifecyclePhase;
import com.uuah.server.lifecycle.phases.NotInLifecyclePhase;
import com.uuah.utils.StringUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午01:51:53
 * @version 1.0.0
 */
public class GenericLifecycleManager implements ILifecycleManager {
	private transient final Logger logger = LoggerFactory
			.getLogger(GenericLifecycleManager.class);
	protected static final NotInLifecyclePhase notInLifecyclePhase = new NotInLifecyclePhase();
	protected String currentPhase = notInLifecyclePhase.getName();
	protected String executingPhase = null;
	protected ListOrderedSet lifecycles = new ListOrderedSet();
	protected Map index = new HashMap(6);
	protected Set completedPhases = new HashSet(6);

	public Set getLifecycles() {
		return lifecycles;
	}

	public void setLifecycles(Set lifecycles) {
		for (Iterator iterator = lifecycles.iterator(); iterator.hasNext();) {
			ILifecyclePhase phase = (ILifecyclePhase) iterator.next();
			registerLifecycle(phase);
		}
	}

	public void registerLifecycle(ILifecyclePhase lci) {
		index.put(lci.getName(), new Integer(lifecycles.size()));
		lifecycles.add(lci);
	}

	public void firePhase(IUuahContext uuahContext, String phase)
			throws AbstractUuahException {
		if (currentPhase.equalsIgnoreCase(phase)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Already in lifecycle phase: " + phase);
			}
			return;
		}

		ILifecyclePhase li = lookupPhase(phase);
		if (!li.isPhaseSupported(currentPhase)) {
			throw new IllegalStateException("Lifecycle phase: " + phase
					+ " does not support current phase: " + currentPhase
					+ ". Phases supported are: "
					+ StringUtils.toString(li.getSupportedPhases()));
		}

		try {
			setExecutingPhase(phase);
			li.applyLifecycle(uuahContext.getRegistry().lookupObjects(
					Object.class), currentPhase);
			setCurrentPhase(li);
		} finally {
			setExecutingPhase(null);
		}
	}

	public ILifecyclePhase applyPhase(Collection objects, String phase)
			throws AbstractUuahException {
		ILifecyclePhase li = lookupPhase(phase);
		li.applyLifecycle(objects, currentPhase);
		return li;
	}

	public String getCurrentPhase() {
		return currentPhase;
	}

	public String getExecutingPhase() {
		return executingPhase;
	}

	protected synchronized void setCurrentPhase(ILifecyclePhase phase) {
		completedPhases.add(phase.getName());
		completedPhases.remove(phase.getOppositeLifecyclePhase());
		this.currentPhase = phase.getName();
	}

	protected synchronized void setExecutingPhase(String phase) {
		this.executingPhase = phase;
	}

	public void reset() {
		setExecutingPhase(null);
		completedPhases.clear();
		setCurrentPhase(notInLifecyclePhase);
	}

	public boolean isPhaseComplete(String phaseName) {
		return completedPhases.contains(phaseName);
	}

	public void applyCompletedPhases(Object object)
			throws AbstractUuahException {
		if (logger.isDebugEnabled()) {
			logger.debug("applying lifecycle to " + object);
		}
		ILifecyclePhase lcp;
		String phase;
		Integer phaseIndex;
		for (Iterator iterator = completedPhases.iterator(); iterator.hasNext();) {
			phase = (String) iterator.next();
			phaseIndex = (Integer) index.get(phase);
			lcp = (ILifecyclePhase) lifecycles.get(phaseIndex.intValue());

			if (logger.isDebugEnabled()) {
				logger.debug("phase: " + lcp);
			}
			lcp.applyLifecycle(object);
		}
		if (getExecutingPhase() != null) {
			phaseIndex = (Integer) index.get(getExecutingPhase());
			lcp = (ILifecyclePhase) lifecycles.get(phaseIndex.intValue());

			if (logger.isDebugEnabled()) {
				logger.debug("and executing: " + lcp);
			}
			lcp.applyLifecycle(object);
		}
	}

	public void checkPhase(String name) throws IllegalStateException {
		if (completedPhases.contains(name)) {
			throw new IllegalStateException("Phase '" + name
					+ "' has already been executed");
		}

		if (name.equalsIgnoreCase(executingPhase)) {
			throw new IllegalStateException("Phase '" + name
					+ "' is already currently being executed");
		}

		if (executingPhase != null) {
			throw new IllegalStateException(
					"Currently executing lifecycle phase: " + executingPhase);
		}

		Integer phaseIndex = (Integer) index.get(name);
		if (phaseIndex == null) {
			throw new IllegalStateException("Phase does not exist: " + name);
		}
		if (NotInLifecyclePhase.PHASE_NAME.equals(currentPhase)) {
			if (phaseIndex.intValue() > 0) {
				throw new IllegalStateException(
						"The first lifecycle phase has to be called before the '"
								+ name + "' phase");
			}
		} else {
			ILifecyclePhase phase = (ILifecyclePhase) lifecycles.get(phaseIndex
					.intValue());
			if (!phase.isPhaseSupported(currentPhase)) {
				throw new IllegalStateException("Lifecycle phase: "
						+ currentPhase + " does not support current phase: "
						+ name + ". Phases supported are: "
						+ StringUtils.toString(phase.getSupportedPhases()));
			}
		}
	}

	protected ILifecyclePhase lookupPhase(String phase)
			throws IllegalArgumentException {
		Integer phaseIndex = (Integer) index.get(phase);
		if (phaseIndex == null) {
			throw new IllegalArgumentException(
					"No lifecycle phase registered with name: " + phase);
		}
		return (ILifecyclePhase) lifecycles.get(phaseIndex.intValue());
	}
}
